#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Create by zhang
# Create on 2022/6/27 20:49
from typing import List

import pdfplumber
import re
import os

from domain.report.common.currency_type import CurrencyType
from domain.report.common.extract_content_enum import ExtractContentEnum
from domain.report.common.file_type_enum import FileTypeEnum
from domain.report.common.money_unit import MoneyType
from domain.report.common.period_enum import PeriodEnum
from domain.report.common.period_mapper import PeriodMapper
from domain.report.dto.balance_sheet_dto import BalanceSheetDTO, BalanceSheetSchema
from domain.report.dto.cash_dto import CashDTO, CashSchema
from domain.report.dto.profit_dto import ProfitDTO, ProfitSchema
from domain.report.extractor.balance_sheet.balancesheet_filter import BalanceSheetFilter
from domain.report.extractor.balance_sheet.consolidated_balancesheet_filter import ConsolidatedBalanceSheetFilter
from domain.report.extractor.cash.cash_filter import CashFilter
from domain.report.extractor.cash.consolidate_cash_filter import ConsolidatedCashFilter
from domain.report.extractor.profit.consolidate_profit_filter import ConsolidatedProfitFilter
from domain.report.extractor.profit.profit_filter import ProfitFilter
from domain.report.extractor.request import Request
from infrastructure.filter.filter import FilterChain
from infrastructure.util.file_util import check_file_type
from domain.report.repository.report_pdf_repository import get_pdf
from config.config import config
_report_pdf_base = config.get("data", "report_pdf")


def _extract(pdf_path:str):
    if check_file_type(pdf_path, [FileTypeEnum.PDF]):
        filter_chain = FilterChain()
        filter_chain.append(ConsolidatedBalanceSheetFilter())
        filter_chain.append(BalanceSheetFilter())
        filter_chain.append(ConsolidatedProfitFilter())
        filter_chain.append(ProfitFilter())
        filter_chain.append(ConsolidatedCashFilter())
        filter_chain.append(CashFilter())
        request = Request()
        with pdfplumber.open(pdf_path) as pdf:
            for p in pdf.pages:
                filter_chain.filter(request.add_page(p))
        return request.data
    return None


def _get_item_from_str(title:str):
    pattern = re.compile(r'^(\d{6})_(一季度|半年|三季度|年度)_(\d{4})_(\d{8}).pdf')
    return pattern.match(title).groups()

def _base_convert(stock_code:str, year:int, period:PeriodEnum, currency:CurrencyType, money_unit:MoneyType, data:list, schema_class, dto_class, is_consolidate=False):
    if data is None or len(data) == 0:
        return None
    dto = dto_class()
    dto.stock_code = stock_code
    dto.stock_id = int(stock_code.split(".")[0])
    dto.year = year
    dto.set_period = period
    dto.is_consolidate = is_consolidate
    head = data[0]
    for item in data[1:]:
        schema_class.set_by_title(dto, item[0], item[2])
    return dto


def _convert_to_balance_dto(stock_code:str, year:int, period:PeriodEnum, currency:CurrencyType, money_unit:MoneyType, data:list ,is_consolidate=False):
    return _base_convert(stock_code=stock_code, year=year, period=period, currency=currency, money_unit=money_unit, data=data, schema_class=BalanceSheetSchema, dto_class=BalanceSheetDTO,
                         is_consolidate=True)


def _convert_to_profit_dto(stock_code:str, year:int, period:PeriodEnum, currency:CurrencyType, money_unit:MoneyType, data:list, is_consolidate=False):
    return _base_convert(stock_code=stock_code, year=year, period=period, currency=currency, money_unit=money_unit,
                         data=data, schema_class=ProfitSchema, dto_class=ProfitDTO,
                         is_consolidate=True)


def _convert_to_cash_dto(stock_code:str, year:int, period:PeriodEnum, currency:CurrencyType, money_unit:MoneyType, data:list, is_consolidate=False):
    return _base_convert(stock_code=stock_code, year=year, period=period, currency=currency, money_unit=money_unit,
                         data=data, schema_class=CashSchema, dto_class=CashDTO,
                         is_consolidate=True)


def _convert_to_consolidate_balance_dto(stock_code:str, year:int, period:PeriodEnum, currency:CurrencyType, money_unit:MoneyType, data:list):
    return _convert_to_balance_dto(stock_code=stock_code, year=year, period=period, currency=currency, money_unit=money_unit, data=data, is_consolidate=True)


def _convert_to_consolidate_profit_dto(stock_code:str, year:int, period:PeriodEnum, currency:CurrencyType, money_unit:MoneyType, data:list):
    return _convert_to_profit_dto(stock_code=stock_code, year=year, period=period, currency=currency, money_unit=money_unit, data=data, is_consolidate=True)


def _convert_to_consolidate_cash_dto(stock_code:str, year:int, period:PeriodEnum, currency:CurrencyType, money_unit:MoneyType, data:list):
    return _convert_to_cash_dto(stock_code=stock_code, year=year, period=period, currency=currency, money_unit=money_unit, data=data, is_consolidate=True)

_converter_select_map = {
    ExtractContentEnum.ConsolidatedBalanceSheet.name: _convert_to_consolidate_balance_dto,
    ExtractContentEnum.BalanceSheet.name: _convert_to_balance_dto,
    ExtractContentEnum.ConsolidatedProfit.name: _convert_to_consolidate_profit_dto,
    ExtractContentEnum.Profit.name: _convert_to_profit_dto,
    ExtractContentEnum.ConsolidatedCash.name: _convert_to_consolidate_cash_dto,
    ExtractContentEnum.Cash.name: _convert_to_cash_dto,
}


def _convert_to_dto(stock_code:str, year:int, period:PeriodEnum, data:dict):
    dto_list = list()
    if data is not None and len(data) > 0:
        for key, value in data.items():
            if value.data and len(value.data) > 0:
                currency:CurrencyType = value.data.get("currency", None)
                money_unit:MoneyType = value.data.get("money_unit", None)
                data_list = value.data.get(key, list())
                dto = _converter_select_map[key](stock_code, year, period, currency, money_unit, data_list)
                if dto is not None:
                    dto_list.append(dto)
    return dto_list


def extract_dto(stock_code:str, year:int, period:PeriodEnum=PeriodEnum.year):
    pdf_list = get_pdf(stock_code, year, period)
    report_list = list()
    if pdf_list is not None and len(pdf_list) > 0:
        for pdf in pdf_list:
            stock_id, period_zh, year, date = _get_item_from_str(pdf)
            period1 = PeriodMapper.get_local_period(period_zh)
            if period == PeriodEnum.all or period == period1:
                data = _extract(os.path.join(_report_pdf_base, stock_id, pdf))
                report_list.append(_convert_to_dto(stock_code, year, period1, data))
    return report_list

